package festival.game;

import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

import festival.board.Board;
import festival.board.ObservableModel;
import festival.input.Action;
import festival.input.Alert;
import festival.input.InputListener;
import festival.palace.PalaceCard;
import festival.palace.PalaceFestival;
import festival.player.Player;

public class Game implements ObservableModel {
    
    private static int[][] sBorderMap = new int[][] { { 0, -7 }, { -1, -6 },
        { -2, -5 }, { -3, -4 }, { -4, -4 }, { -5, -3 }, { -5, -2 },
        { -6, -1 }, { -7, -1 }, { -8, 0 }, { -8, 1 }, { -9, 2 }, { -8, 2 },
        { -8, 3 }, { -7, 3 }, { -7, 4 }, { -7, 5 }, { -6, 4 }, { -5, 3 },
        { -4, 3 }, { -4, 4 }, { -4, 5 }, { -3, 4 }, { -2, 3 }, { -1, 3 },
        { -1, 4 }, { -1, 5 }, { 0, 4 }, { 1, 3 }, { 2, 3 }, { 2, 4 },
        { 2, 5 }, { 3, 4 }, { 4, 3 }, { 5, 3 }, { 6, 2 }, { 7, 2 },
        { 7, 1 }, { 8, 0 }, { 8, -1 }, { 7, -1 }, { 7, -2 }, { 8, -3 },
        { 8, -4 }, { 7, -4 }, { 7, -5 }, { 7, -6 }, { 7, -7 }, { 6, -7 },
        { 5, -6 }, { 4, -5 }, { 3, -5 }, { 3, -6 }, { 2, -5 }, { 1, -5 },
        { 1, -6 }, { 1, -7 } };

	private Player[] mPlayers;
	private int mCurrentPlayerId;
	private int mActionPoints;
	private boolean mHasPlacedLandTile;
	private boolean mPalaceFestivalFlagged;
	private Stack<PalaceCard> mDeck;
	private Stack<PalaceCard> mDiscardPile;
	private PalaceCard mFestivalCard;
	private int mPalaceCardsDrawn;
	private int mNumThreeTiles;
	
	private TurnChangeListener mTurnChangeListener;
	
	private ModelChangeListener mModelChangeListener;
	
	private GameStateChangeListener mGameStateChangeListener;
	private GameState mGameState;
	
	private Board mBoard;
	
	private GameState mTurnState;
	private GameState mPlaceTileState;
	private GameState mPlacePalaceTileState;
	private GameState mTabPalacesState;
	private GameState mPlaceDevState;
	private GameState mTabDevsState;
	private GameState mMoveDevState;
	
	public Game(int numPlayers) {
		System.out.println("Created new game with " + numPlayers + "!");
		mActionPoints = 6;
		initPlayers(numPlayers);
		initBoard();
		initGameStates();
		initDeck();
		startTurn();
		
		mNumThreeTiles = 56;
	}
	
	private void initPlayers(int numPlayers) {
		mPlayers = new Player[numPlayers];
		for (int i = 0; i < numPlayers; i++)
			mPlayers[i] = new Player(i);
		
		mCurrentPlayerId = 0;
	}
	
	public Player[] getPlayers() {
	    return mPlayers;
	}
	
	public Board getBoard() {
	    return mBoard;
	}
	
	private void initBoard() {
		//TODO
	    mBoard = new Board(sJavaMap);
//	    Board b = new Board(sJavaMap);
//	    mBoard = new Board(b.updateBorderMap());
	}
	
	private void initGameStates() {
		/*
		 * State that allows player to do any of the available moves. If a move is
		 * not allowed, we need to alert the player.
		 */
	    mTurnState = new GameState()
	    .addInputListener(new InputListener(KeyEvent.VK_V, new Action() {
	        public void doAction() {
	            beginPlaceVillageTile();
	            changeState(mPlaceTileState);
	    	    System.out.println("Changing to mPlaceTileState");
	        }
	    })).addInputListener(new InputListener(KeyEvent.VK_I, new Action() {
            public void doAction() {
                beginPlaceIrrigationTile();
                changeState(mPlaceTileState);
	    	    System.out.println("Changing to mPlaceTileState");
            }
        })).addInputListener(new InputListener(KeyEvent.VK_P, new Action() {
            public void doAction() {
                queryForPalaceTileSize(); //implement me
            }
        })).addInputListener(new InputListener(KeyEvent.VK_R, new Action() {
            public void doAction() {
                beginPlaceRiceTile();
                changeState(mPlaceTileState);
	    	    System.out.println("Changing to mPlaceTileState");
            }
        })).addInputListener(new InputListener(KeyEvent.VK_2, new Action() {
            public void doAction() {
                mBoard.beginPlaceTwoTile();
                changeState(mPlaceTileState);
            }
        })).addInputListener(new InputListener(KeyEvent.VK_3, new Action() {
            public void doAction() {
                mBoard.beginPlaceThreeTile();
                mNumThreeTiles--;
                mHasPlacedLandTile = true;
                changeState(mPlaceTileState);
            }
        })).addInputListener(new InputListener(KeyEvent.VK_D, new Action() {
            public void doAction() {
                // place developer
                mBoard.beginPlaceDeveloper(mCurrentPlayerId);
                changeState(mPlaceDevState);
	    	    System.out.println("Changing to mPlaceDevState");
            }
        })).addInputListener(new InputListener(KeyEvent.VK_S, new Action() {
            public void doAction() {
                // move developer
                
                // we need to switch states here
            }
        })).addInputListener(new InputListener(KeyEvent.VK_B, new Action() {
            public void doAction() {
                // build palace
            }
        })).addInputListener(new InputListener(KeyEvent.VK_E, new Action() {
            public void doAction() {
                // enlarge palace
            }
        })).addInputListener(new InputListener(KeyEvent.VK_C, new Action() {
            public void doAction() {
                if (spendAP(1) && playerDrawCard());
                notifyChanged();
            }
        })).addInputListener(new InputListener(KeyEvent.VK_T, new Action() {
            public void doAction() {
                if(mPlayers[mCurrentPlayerId].useActionToken()) {
                	mActionPoints++;
                	notifyChanged();
                }
                
            }
        })).addInputListener(new InputListener(KeyEvent.VK_Q, new Action() {
            public void doAction() {
                mBoard.tabThroughDevelopers(mCurrentPlayerId);
                changeState(mTabDevsState);
	    	    System.out.println("Changing to mTabDevsState");
            }
        })).addInputListener(new InputListener(KeyEvent.VK_W, new Action() {
            public void doAction() {
                mBoard.tabThroughPalaces();
                changeState(mTabPalacesState);
	    	    System.out.println("Changing to mTabPalacesState");
            }
        })).addInputListener(new InputListener(KeyEvent.VK_X, new Action() {
            public void doAction() {
	    	    System.out.println("endingTurn");
                endTurn();
            }
        }));
	    
	    mPlaceTileState = new GameState()
	    .addInputListener(new InputListener(KeyEvent.VK_UP, new Action() {
            public void doAction() {
                mBoard.moveOverlayTile(Board.NORTHWEST);
            }
	    })).addInputListener(new InputListener(KeyEvent.VK_RIGHT, new Action() {
            public void doAction() {
                mBoard.moveOverlayTile(Board.EAST);
            }
        })).addInputListener(new InputListener(KeyEvent.VK_DOWN, new Action() {
            public void doAction() {
                mBoard.moveOverlayTile(Board.SOUTHEAST);
            }
        })).addInputListener(new InputListener(KeyEvent.VK_LEFT, new Action() {
            public void doAction() {
                mBoard.moveOverlayTile(Board.WEST);
            }
        })).addInputListener(new InputListener(KeyEvent.VK_ENTER, new Action() {
            public void doAction() {
                if(mBoard.placeTile() && spendAP(1)) {
                    changeState(mTurnState);
                }
            }
        })).addInputListener(new InputListener(KeyEvent.VK_ESCAPE, new Action() {
            public void doAction() {
                mBoard.clearPlacementOverlay();
                changeState(mTurnState);
	    	    System.out.println("Changing to mTurnState");
            }
        })).addInputListener(new InputListener(KeyEvent.VK_R, new Action() {
            public void doAction() {
                mBoard.rotatePlacement(-1);
                changeState(mPlaceTileState);
            }
        })).addInputListener(new InputListener(KeyEvent.VK_E, new Action() {
            public void doAction() {
                mBoard.rotatePlacement(1);
                changeState(mPlaceTileState);
            }
        }));
	    
	    mPlacePalaceTileState = new GameState()
	    .addInputListener(new InputListener(KeyEvent.VK_UP, new Action() {
            public void doAction() {
                mBoard.moveOverlayPalaceTile(Board.NORTHWEST);
            }
	    })).addInputListener(new InputListener(KeyEvent.VK_RIGHT, new Action() {
            public void doAction() {
                mBoard.moveOverlayPalaceTile(Board.EAST);
            }
        })).addInputListener(new InputListener(KeyEvent.VK_DOWN, new Action() {
            public void doAction() {
                mBoard.moveOverlayPalaceTile(Board.SOUTHEAST);
            }
        })).addInputListener(new InputListener(KeyEvent.VK_LEFT, new Action() {
            public void doAction() {
                mBoard.moveOverlayPalaceTile(Board.WEST);
            }
        })).addInputListener(new InputListener(KeyEvent.VK_ENTER, new Action() {
            public void doAction() {
                if(mBoard.placePalaceTile(mCurrentPlayerId) && spendAP(1)) {
                    changeState(mTurnState);
                }
            }
        })).addInputListener(new InputListener(KeyEvent.VK_ESCAPE, new Action() {
            public void doAction() {
                mBoard.clearPlacementOverlay();
                changeState(mTurnState);
	    	    System.out.println("Changing to mTurnState");
            }
        }));
	    
	    mTabPalacesState = new GameState()
        .addInputListener(new InputListener(KeyEvent.VK_W, new Action() {
            public void doAction() {
                mBoard.tabThroughPalaces();
            }
        })).addInputListener(new InputListener(KeyEvent.VK_ENTER, new Action() {
            public void doAction() {
            	queryUpgradePalace();
            }
        })).addInputListener(new InputListener(KeyEvent.VK_ESCAPE, new Action() {
            public void doAction() {
                mBoard.clearSelectedPalace();
                changeState(mTurnState);
	    	    System.out.println("Changing to mTurnState");
            }
        }));
	    
	    mPlaceDevState = new GameState()
        .addInputListener(new InputListener(KeyEvent.VK_RIGHT, new Action() {
            public void doAction() {
                mBoard.moveDeveloperPlacement(Board.CLOCKWISE);
            }
        })).addInputListener(new InputListener(KeyEvent.VK_LEFT, new Action() {
            public void doAction() {
                mBoard.moveDeveloperPlacement(Board.COUNTERCLOCKWISE);
            }
        })).addInputListener(new InputListener(KeyEvent.VK_ENTER, new Action() {
            public void doAction() {
            	if (mBoard.placeDeveloper() && spendAP(1)) {
            		changeState(mTurnState);
            		System.out.println("Changing to mTurnState");
            	}
            }
        })).addInputListener(new InputListener(KeyEvent.VK_ESCAPE, new Action() {
            public void doAction() {
                mBoard.clearPlacementOverlay();
                changeState(mTurnState);
	    	    System.out.println("Changing to mTurnState");
            }
        }));
	    
	    mTabDevsState = new GameState()
        .addInputListener(new InputListener(KeyEvent.VK_Q, new Action() {
            public void doAction() {
                mBoard.tabThroughDevelopers(mCurrentPlayerId);
            }
        })).addInputListener(new InputListener(KeyEvent.VK_ENTER, new Action() {
            public void doAction() {
                mBoard.beginMoveDeveloper(mCurrentPlayerId);
//                mBoard.clearSelectedDeveloper();
                changeState(mMoveDevState);
	    	    System.out.println("Changing to mMoveDevsState");
            }
        })).addInputListener(new InputListener(KeyEvent.VK_ESCAPE, new Action() {
            public void doAction() {
                mBoard.clearSelectedDeveloper();
                changeState(mTurnState);
	    	    System.out.println("Changing to mTurnState");
            }
        }));
	    
	    mMoveDevState = new GameState()
	    .addInputListener(new InputListener(KeyEvent.VK_UP, new Action() {
            public void doAction() {
                mBoard.moveDeveloperMovementTarget(Board.NORTHWEST);
            }
	    })).addInputListener(new InputListener(KeyEvent.VK_RIGHT, new Action() {
            public void doAction() {
                mBoard.moveDeveloperMovementTarget(Board.EAST);
            }
        })).addInputListener(new InputListener(KeyEvent.VK_DOWN, new Action() {
            public void doAction() {
                mBoard.moveDeveloperMovementTarget(Board.SOUTHEAST);
            }
        })).addInputListener(new InputListener(KeyEvent.VK_LEFT, new Action() {
            public void doAction() {
                mBoard.moveDeveloperMovementTarget(Board.WEST);
            }
        })).addInputListener(new InputListener(KeyEvent.VK_ENTER, new Action() {
            public void doAction() {
                if (mBoard.moveDeveloper() && spendAP(1)){
                changeState(mTabDevsState);
	    	    System.out.println("Changing to mTabDevsState");}
            }
        })).addInputListener(new InputListener(KeyEvent.VK_ESCAPE, new Action() {
            public void doAction() {
                mBoard.clearDeveloperMovement();
                changeState(mTurnState);
	    	    System.out.println("Changing to mTurnState");
            }
        }));
	    
	    
	    changeState(mTurnState);
	}
	
	private void initDeck() {
		List<PalaceCard> cards = new ArrayList<PalaceCard>();
		for (int i = 0; i < 5; i++) {
			cards.add(new PalaceCard(new int[] {PalaceCard.Son}));
			cards.add(new PalaceCard(new int[] {PalaceCard.Father}));
			cards.add(new PalaceCard(new int[] {PalaceCard.HolySpirit}));
			cards.add(new PalaceCard(new int[] {PalaceCard.Son, PalaceCard.Father}));
			cards.add(new PalaceCard(new int[] {PalaceCard.Son, PalaceCard.HolySpirit}));
			cards.add(new PalaceCard(new int[] {PalaceCard.Father, PalaceCard.HolySpirit}));
		}
		Collections.shuffle(cards);
		mDeck = new Stack<PalaceCard>();
		mDeck.addAll(cards);
		mFestivalCard = drawCard();
		
		for (Player p : mPlayers) {
			p.addPalaceCard(drawCard());
			p.addPalaceCard(drawCard());
			p.addPalaceCard(drawCard());
		}
	}
	
	private PalaceCard drawCard() {
		if (mDeck.empty())
			return null;
		
		PalaceCard card = mDeck.peek();
		mDeck.pop();
		return card;
	}
	
	public boolean playerDrawCard() {
		if (mDeck.isEmpty())
			return false;
		mPlayers[mCurrentPlayerId].addPalaceCard(drawCard());
		return true;
	}
	
	private void holdPalaceFestival() {
		new PalaceFestival(mPlayers, mCurrentPlayerId, mFestivalCard, 2); // last int needs to be palace size
	}
	
	// Game Methods
	public boolean endTurn() {
		if (canHoldPalaceFestival()) {
			queryPalaceFestival();
		}

		mCurrentPlayerId++;
		mCurrentPlayerId %= mPlayers.length;
		
		startTurn();
		
		return true;
	}
	
	private PalaceCard getFestivalCard() {
	    return mFestivalCard;
	}
	
	private boolean canHoldPalaceFestival() {
		// Check card requirement
		List<PalaceCard> cards = mPlayers[mCurrentPlayerId].getHand();
		boolean hasCardToPlay = false;
		for (PalaceCard card : cards)
			if (mFestivalCard.compareTo(card) > 0)
				hasCardToPlay = true;
		if (!hasCardToPlay) {
			System.out.println("cant hold PF bc failed to meet card reqs");
			return false;
		}
		
		// Check developer requirement
		if (!mBoard.canHoldPalaceFestival(mCurrentPlayerId)) {
			System.out.println("cant hold PF bc failed to meet dev reqs");
			return false;
		}
		
		return true;
	}
	

	public void queryUpgradePalace() {
		InputListener[] inputs = new InputListener[] {
	            new InputListener(KeyEvent.VK_4, new Action() {
	                public void doAction() {
	                    // place level 4
	                    upgradePalace(4);
	                }
	            }),
	            new InputListener(KeyEvent.VK_6, new Action() {
	                public void doAction() {
	                    // place level 6
	                    upgradePalace(6);
	                }
	            }),
	            new InputListener(KeyEvent.VK_8, new Action() {
	                public void doAction() {
	                    // place level 8
	                	upgradePalace(8);
	                }
	            }),
	            new InputListener(KeyEvent.VK_ESCAPE, new Action() {
	                public void doAction() {
	                    // cancel placement
	                }
	            })
	        };
	        String[] options = new String[] {
	                "Level 4",
	                "Level 6",
	                "Level 8",
	                "Cancel"
	        };
	        new Alert("Upgrade Palace", "Would you like to upgrade this palace?",
	                options, inputs).show();
	}
	
	public void upgradePalace(int newPalaceSize) {
		if (mBoard.upgradePalace(mCurrentPlayerId, newPalaceSize)) {
			mPlayers[mCurrentPlayerId].addVictoryPoints(newPalaceSize / 2);
    		changeState(mTurnState);
		}
	}
	
	private void queryPalaceFestival() {
		//TODO
		System.out.println("I'm querying a palaceFestival right now! xD");
		InputListener doFestival = new InputListener(KeyEvent.VK_1, new Action() {
            public void doAction() {
                System.out.println("I want to arrange a festival");
            }
        });
		InputListener cancel = new InputListener(KeyEvent.VK_1, new Action() {
            public void doAction() {
                System.out.println("no festival for me :(");
            }
        });
		InputListener[] inputs = new InputListener[] {
		    doFestival, cancel
		};
		String[] options = new String[] {
		        "Arrange festival",
		        "No thanks"
		};
		new Alert("Palace festival", "Would you like to arrange a palace festival?",
		        options, inputs).show();
	}
	
	private void startTurn() {
		mActionPoints = 6;
		mHasPlacedLandTile = false;
		mPalaceFestivalFlagged = false;
		mPalaceCardsDrawn = 0;
		notifyTurnChanged();
	}
	
	private boolean canAffordActionWithoutToken(int actionPointsRequired) {
		int availableActionPoints = mActionPoints;
		if (!mHasPlacedLandTile)
			availableActionPoints--;
		
		return (availableActionPoints >= actionPointsRequired);
	}
	
	private boolean canAffordActionPointsWithToken(int actionPointsRequired) {
		int availableActionPoints = mActionPoints;
		if (!mHasPlacedLandTile)
			availableActionPoints--;
		if (mPlayers[mCurrentPlayerId].getActionTokenCount() > 0)
			availableActionPoints++;
		
		return (availableActionPoints >= actionPointsRequired);
	}
	
	// Board Methods
	
	public void beginPlaceVillageTile() {
	    mBoard.beginPlaceVillageTile();
	}
	
	public void beginPlaceIrrigationTile() {
        mBoard.beginPlaceIrrigationTile();
    }
	
	public void queryForPalaceTileSize() {
        InputListener[] inputs = new InputListener[] {
            new InputListener(KeyEvent.VK_2, new Action() {
                public void doAction() {
                    // place level 2
                    beginPlacePalaceTile(2);
                    System.out.println("pressed 2");
                }
            }),
            new InputListener(KeyEvent.VK_4, new Action() {
                public void doAction() {
                    // place level 4
                    beginPlacePalaceTile(4);
                }
            }),
            new InputListener(KeyEvent.VK_6, new Action() {
                public void doAction() {
                    // place level 6
                    beginPlacePalaceTile(6);
                }
            }),
            new InputListener(KeyEvent.VK_8, new Action() {
                public void doAction() {
                    // place level 8
                    beginPlacePalaceTile(8);
                }
            }),
            new InputListener(KeyEvent.VK_ESCAPE, new Action() {
                public void doAction() {
                    // cancel placement
                }
            })
        };
        String[] options = new String[] {
                "Level 2",
                "Level 4",
                "Level 6",
                "Level 8",
                "Cancel"
        };
        new Alert("Palace festival", "Would you like to arrange a palace festival?",
                options, inputs).show();
	}
	
	public void beginPlacePalaceTile(int palaceSize) {
	    mBoard.beginPlacePalaceTile(mCurrentPlayerId, palaceSize);
	    changeState(mPlacePalaceTileState);
        System.out.println("Changing to mPlacePalaceTileState");
	}
	
	public void beginPlaceRiceTile() {
	    mBoard.beginPlaceRiceTile();
	    notifyChanged();
	}
	
	private void changeState(GameState l) {
	    mGameState = l;
	    notifyGameStateChanged();
	}
	
	public GameState getGameState() {
	    return mGameState;
	}
	
	public interface GameStateChangeListener {
	    public void onGameStateChange(GameState l);
	}
	
	public void setGameStateChangeListener(GameStateChangeListener l) {
	    mGameStateChangeListener = l;
	}
	
	public void notifyGameStateChanged() {
	    if(mGameStateChangeListener != null) {
	        mGameStateChangeListener.onGameStateChange(mGameState);
	    }
	}
	
	public interface TurnChangeListener {
	    public void onTurnChange(int newPlayerIndex);
	}
	
	public void setTurnChangeListener(TurnChangeListener l) {
	    mTurnChangeListener = l;
	}
	
	public void notifyTurnChanged() {
	    if(mTurnChangeListener != null) {
	        mTurnChangeListener.onTurnChange(mCurrentPlayerId);
	    }
	}
	
	private void playErrorSound() {
		Clip sound;
		try {
			AudioInputStream audio = AudioSystem.getAudioInputStream(new File("bin/resources/error.wav"));
			sound = AudioSystem.getClip();
			sound.open(audio);
			sound.loop(0);
			sound.start();
		} catch (UnsupportedAudioFileException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (LineUnavailableException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public int getCurrentActionPointCount() {
	    return mActionPoints;
	}
	
	private static int[][] sJavaMap = new int[][] {
        {-9, 2},                                                                                                                // x = -9
        {-8, 0}, {-8, 1}, {-8, 2}, {-8, 3},                                                                                     // x = -8
        {-7, -1}, {-7, 0}, {-7, 1}, {-7, 2}, {-7, 3}, {-7, 4}, {-7, 5},                                                         // x = -7
        {-6, -1}, {-6, 0}, {-6, 1}, {-6, 2}, {-6, 3}, {-6, 4},                                                                  // x = -6
        {-5, -3}, {-5, -2}, {-5, -1}, {-5, 0}, {-5, 1}, {-5, 2}, {-5, 3},                                                       // x = -5
        {-4, -4}, {-4, -3}, {-4, -2}, {-4, -1}, {-4, 0}, {-4, 1}, {-4, 2}, {-4, 3}, {-4, 4}, {-4, 5},                           // x = -4
        {-3, -4}, {-3, -3}, {-3, -2}, {-3, -1}, {-3, 0}, {-3, 1}, {-3, 2}, {-3, 3}, {-3, 4},                                    // x = -3
        {-2, -5}, {-2, -4}, {-2, -3}, {-2, -2}, {-2, -1}, {-2, 0}, {-2, 1}, {-2, 2}, {-2, 3},                                   // x = -2
        {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5},       // x = -1
        {0, -7}, {0, -6}, {0, -5}, {0, -4}, {0, -3}, {0, -2}, {0, -1}, {0, 0}, {0, 1}, {0, 2}, {0, 3}, {0, 4},                  // x =  0
        {1, -7}, {1, -6}, {1, -5}, {1, -4}, {1, -3}, {1, -2}, {1, -1}, {1, 0}, {1, 1}, {1, 2}, {1, 3},                          // x =  1
        {2, -5}, {2, -4}, {2, -3}, {2, -2}, {2, -1}, {2, 0}, {2, 1}, {2, 2}, {2, 3}, {2, 4}, {2, 5},                            // x =  2
        {3, -6}, {3, -5}, {3, -4}, {3, -3}, {3, -2}, {3, -1}, {3, 0}, {3, 1}, {3, 2}, {3, 3}, {3, 4},                           // x =  3
        {4, -5}, {4, -4}, {4, -3}, {4, -2}, {4, -1}, {4, 0}, {4, 1}, {4, 2}, {4, 3},                                            // x =  4
        {5, 3}, {5, -6}, {5, -5}, {5, -4}, {5, -3}, {5, -2}, {5, -1}, {5, 0}, {5, 1}, {5, 2},                                   // x =  5
        {6, -7}, {6, -6}, {6, -5}, {6, -4}, {6, -3}, {6, -2}, {6, -1}, {6, 0}, {6, 1}, {6, 2},                                  // x =  6
        {7, -7}, {7, -6}, {7, -5}, {7, -4}, {7, -3}, {7, -2}, {7, -1}, {7, 0}, {7, 1}, {7, 2},                                  // x =  7
        {8, -4}, {8, -3}, {8, -1}, {8, 0}                                                                                       // x =  8
	};

    @Override
    public void addModelChangeListener(ModelChangeListener l) {
        mModelChangeListener = l;
    }

    @Override
    public void notifyChanged() {
        if (mModelChangeListener != null) {
            mModelChangeListener.onModelChange(this);
        }
    }
    
    public int getNumThreeTiles() {
    	return mNumThreeTiles;
    }
    
    public int getNumCardsInDeck() {
    	return mDeck.size();
    }
    
    public boolean spendAP(int amount) {
    	if (mActionPoints < amount || (mActionPoints == amount && !mHasPlacedLandTile))
    		return false;
    	mActionPoints -= amount;
    	return true;
    }
}
